﻿
// QuickCMDDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "QuickCMD.h"
#include "QuickCMDDlg.h"
#include "afxdialogex.h"
#include "version.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define MARGIN_TOP		30		// 顶部间距
#define MARGIN			10		// 边距
#define MARGIN_X2		20		// 两倍边距
#define BAR_WIDTH		300		// 工具宽度
#define REFRESH_SEC		1		// 刷新时间（秒）

// 控制台窗口类
#define CMD_CLS		_T("ConsoleWindowClass")

// cmd信息
std::map<HWND, CString> g_cmdMap;

// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CQuickCMDDlg 对话框

CQuickCMDDlg::CQuickCMDDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_QUICKCMD_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_pListBox = NULL;
	m_nScrWidth = 0;
	m_nScrHeight = 0;
	m_sAppDataPath = "";
	m_sConfigFile = "";
}

void CQuickCMDDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CQuickCMDDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_SIZE()
	ON_LBN_SELCHANGE(IDC_LIST3, &CQuickCMDDlg::OnSelchangeList)
	ON_LBN_DBLCLK(IDC_LIST3, &CQuickCMDDlg::OnDblclkList)
	ON_WM_TIMER()
	ON_WM_ACTIVATEAPP()
	ON_WM_CLOSE()
END_MESSAGE_MAP()


// CQuickCMDDlg 消息处理程序

BOOL CQuickCMDDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时，框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	this->m_sAppDataPath = GetAppDataPath();
	this->m_sConfigFile = this->m_sAppDataPath + L"\\QuickCMD\\QuickCMD.ini";
	this->m_nScrWidth = GetSystemMetrics(SM_CXFULLSCREEN);
	this->m_nScrHeight = GetSystemMetrics(SM_CYFULLSCREEN);

	this->m_pListBox = (CListBox *)GetDlgItem(IDC_LIST3);
	
	this->ResetPos(true);
	this->ResizeMain();
	this->RefreshList();

	this->SetTimer(1, REFRESH_SEC * 1000, NULL);

	return TRUE;  // 除非将焦点设置到控件，否则返回 TRUE
}

void CQuickCMDDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else if ((nID & 0xFFF0) == SC_MINIMIZE) {
		CDialogEx::OnSysCommand(nID, lParam);
		// 
		int len = m_pListBox->GetCount();
		for (int x = 0; x < len; ++x) {
			LPARAM lpData = this->m_pListBox->GetItemData(x);
			HWND hWnd = (HWND)lpData;

			if (!::IsWindow(hWnd)) {
				this->m_pListBox->DeleteString(x);
				return;
			}

			::PostMessage(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, NULL);
		}
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮，则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序，
//  这将由框架自动完成。

void CQuickCMDDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CQuickCMDDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CQuickCMDDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	// TODO: 在此处添加消息处理程序代码
	this->ResizeMain();
}


void CQuickCMDDlg::ResizeMain()
{
	// TODO: 在此处添加实现代码.
	CRect rt;
	GetClientRect(&rt);

	if (this->m_pListBox->GetSafeHwnd() != NULL) {
		this->m_pListBox->MoveWindow(
			rt.left + MARGIN,
			rt.top + MARGIN,
			rt.Width() - MARGIN_X2,
			rt.Height() - MARGIN_X2,
			TRUE
		);
	}
}

int CQuickCMDDlg::RefreshList()
{
	g_cmdMap.clear();

	// 遍历
	EnumWindows(EnumWindowProc, NULL);

	// 没有数据，则清空
	if (g_cmdMap.size() == 0) {
		this->m_pListBox->ResetContent();
		this->SetWindowTextW(L"快速控制台");
		return 0;
	}

	// 如果数量相等且每一个元素都在列表框内，且标题没有变化，则直接返回
	int nCurLen = this->m_pListBox->GetCount();
	if (nCurLen > 0) {
		int nHit = 0;
		for (int i = 0; i < nCurLen; ++i) {
			HWND hWnd = (HWND)this->m_pListBox->GetItemData(i);
			CString sTitle;
			this->m_pListBox->GetText(i, sTitle);
			if (g_cmdMap.count(hWnd) == 1 && g_cmdMap[hWnd].Compare(sTitle) == 0) {
				nHit++;
			}
		}
		if (nHit == nCurLen && nHit == g_cmdMap.size()) {
			// 全部命中，且数量相等，则证明没有更新，直接返回
			return 0;
		}
	}

	// 刷新列表
	this->m_pListBox->ResetContent();
	std::map<HWND, CString>::iterator iter;
	for (iter = g_cmdMap.begin(); iter != g_cmdMap.end(); ++iter)
	{
		int nIndex = this->m_pListBox->AddString(iter->second);
		this->m_pListBox->SetItemData(nIndex, (LPARAM)iter->first);
		this->m_pListBox->SetItemHeight(nIndex, 17);
	}

	CString sCount;
	sCount.Format(L"%d", g_cmdMap.size());
	sCount = L"快速控制台 - " + sCount + L" 个";
	this->SetWindowTextW(sCount);

	return 0;
}

BOOL CALLBACK CQuickCMDDlg::EnumWindowProc(HWND hWnd, LPARAM lParam)
{
	TCHAR szClass[256] = { 0 };
	TCHAR szWindow[256] = { 0 };
	::GetWindowText(hWnd, szWindow, 255); //获取窗口标题
	::GetClassName(hWnd, szClass, 255); //获取窗口类名称
	if (::IsWindow(hWnd) &&   //判断是否为一个窗口
		::IsWindowVisible(hWnd) &&  //如果指定的窗口及其父窗口具有WS_VISIBLE风格即可见窗口
		(::GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) != WS_EX_TOOLWINDOW &&
		::GetWindowLong(hWnd, GWL_HWNDPARENT) == 0)
	{
		TCHAR* cFind = NULL;
		cFind = wcsstr(szClass, CMD_CLS);
		if (cFind)
		{
			CString sTitle;
			sTitle.Format(_T("%s"), szWindow);
			g_cmdMap[hWnd] = sTitle;
		}
	}
	return TRUE;
}


void CQuickCMDDlg::OnSelchangeList()
{
	int i = this->m_pListBox->GetCurSel();
	LPARAM lpData = this->m_pListBox->GetItemData(i);
	HWND hWnd = (HWND)lpData;

	if (!::IsWindow(hWnd)) {
		this->m_pListBox->DeleteString(i);
		return;
	}

	// 工具栏复位
	this->ResetPos(false);

	// 如果该控制台处于最小化状态，先恢复到正常状态
	if (::IsIconic(hWnd)) {
		::PostMessage(hWnd, WM_SYSCOMMAND, SC_RESTORE, NULL);
	}

	// 切换到选中的CMD
	SwitchToThisWindow(hWnd, true);

	// 移动CMD窗体到工具栏的右边，并且全屏
	CRect rect;
	this->GetWindowRect(&rect);
	int nWidth = rect.Width();

	::MoveWindow(hWnd, nWidth + MARGIN_X2, MARGIN,
		this->m_nScrWidth - nWidth - MARGIN_X2 - MARGIN,
		this->m_nScrHeight - MARGIN_X2, true);

	// 切换回本窗体
	SwitchToThisWindow(this->m_hWnd, true);
}


void CQuickCMDDlg::OnDblclkList()
{
	// 双击关闭
	int i = this->m_pListBox->GetCurSel();
	LPARAM lpData = this->m_pListBox->GetItemData(i);
	HWND hWnd = (HWND)lpData;

	if (!::IsWindow(hWnd)) {
		this->m_pListBox->DeleteString(i);
		return;
	}

	CString sText;
	this->m_pListBox->GetText(i, sText); 
	sText.Format(_T("确认关闭“%s”控制台？"), sText);

	if (IDOK == MessageBox(sText, _T("关闭提示"), MB_OKCANCEL | MB_ICONQUESTION)) {
		::PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, NULL);
	}
}


void CQuickCMDDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CDialogEx::OnTimer(nIDEvent);

	if (this->IsIconic()) {
		return;
	}

	this->RefreshList();
}


void CQuickCMDDlg::ResetPos(bool bResetSize)
{
	if (bResetSize) {

		int nBarWidth = BAR_WIDTH;
		int nBarHeight = this->m_nScrHeight - MARGIN_X2;

		try {
			if (-1 != GetFileAttributes(this->m_sConfigFile)) {
				int nWidth = GetPrivateProfileInt(L"size", L"width", 0, this->m_sConfigFile);
				int nHeight = GetPrivateProfileInt(L"size", L"height", 0, this->m_sConfigFile);
				if (nWidth > 50 && nHeight > 50) {
					nBarWidth = nWidth;
					nBarHeight = nHeight;
				}
			}
			else {
				CString sDir = this->m_sAppDataPath + L"\\QuickCMD";
				if (-1 == GetFileAttributes(sDir)) {
					CreateDirectory(sDir, NULL);
				}
				CTime ct = CTime::GetCurrentTime();;
				CString sTime = ct.Format("%Y%m%d_%H%M%S");
				WritePrivateProfileString(L"size", L"created", sTime, this->m_sConfigFile);
			}
		}
		catch (...) {
			// ignore
		}

		this->MoveWindow(MARGIN, MARGIN, nBarWidth, nBarHeight, true);
	}
	else {
		CRect rect;
		this->GetWindowRect(&rect);
		this->MoveWindow(MARGIN, MARGIN, rect.Width(), rect.Height(), true);
	}
}


void CQuickCMDDlg::OnActivateApp(BOOL bActive, DWORD dwThreadID)
{
	CDialogEx::OnActivateApp(bActive, dwThreadID);
}

void CQuickCMDDlg::OnClose()
{
	// 保存当前的尺寸
	if (!this->IsIconic()) {
		CRect rect;
		this->GetWindowRect(&rect);
		CString sWidth;
		CString sHeight;
		sWidth.Format(L"%d", rect.Width());
		sHeight.Format(L"%d", rect.Height());
		WritePrivateProfileString(L"size", L"width", sWidth, this->m_sConfigFile);
		WritePrivateProfileString(L"size", L"height", sHeight, this->m_sConfigFile);
	}

	CDialogEx::OnClose();
}


CString CQuickCMDDlg::GetAppDataPath()
{
	wchar_t m_lpszDefaultDir[MAX_PATH] = { 0 };
	wchar_t szDocument[MAX_PATH] = { 0 };

	LPITEMIDLIST pList = NULL;
	SHGetSpecialFolderLocation(NULL, CSIDL_LOCAL_APPDATA, &pList);
	if (pList && SHGetPathFromIDList(pList, szDocument))
	{
		GetShortPathName(szDocument, m_lpszDefaultDir, _MAX_PATH);
	}
	return m_lpszDefaultDir;
}


BOOL CQuickCMDDlg::PreTranslateMessage(MSG* pMsg)
{
	//屏蔽ESC关闭窗体/
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) {
		return TRUE;
	}
	else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DELETE) {
		this->DeleteSels();
	}
	else {
		return CDialog::PreTranslateMessage(pMsg);
	}
}


void CQuickCMDDlg::DeleteSels()
{
	// 检查列表框中的选择项，执行批量删除
	int nSelNum = this->m_pListBox->GetSelCount();
	if (nSelNum > 0) {
		CString sTitle;
		sTitle.Format(_T("确认关闭选择的 %d 个控制台？"), nSelNum);

		if (IDOK == MessageBox(sTitle, _T("关闭提示"), MB_OKCANCEL | MB_ICONQUESTION)) {
			int* p_nIndex = new int[nSelNum];
			this->m_pListBox->GetSelItems(nSelNum, p_nIndex);
			for (int i = 0; i < nSelNum; ++i) {
				HWND hWnd = (HWND)this->m_pListBox->GetItemData(p_nIndex[i]);
				::PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, NULL);
			}
		}
	}
}
